//------------------------------------------------------------------------------
//!	@file dsp_base.c
//!	@brief
//!		DSP lab file
//!
//!	@Description
//!		This DSP lab file shall support numerous DSP experiments without
//!		recoding the main application over and over.
//!
//!	$Id: dsp_base.c 163 2013-03-03 07:18:56Z ac.verbeck@gmail.com $
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//	Copyright and Disclaimer Notice
//
//	Copyright (c) 2013 A.C. Verbeck
//
//	This file is part of DSP lab: an experimenters library
//
//	DSP lab is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	DSP lab is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with DSP lab.  If not, see <http://www.gnu.org/licenses/>.
//
//------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <stdint.h>
#include <stdbool.h>

#include <popt.h>
#include <sndfile.h>

#include "bi.h"
#include "prt.h"
#include "dsp.h"

//------------------------------------------------------------------------------
// Local defines
//------------------------------------------------------------------------------
#define OPT_INPUT			0x0001
#define OPT_OUTPUT			0x0002
#define OPT_VOL_DB			0x0004
#define OPT_ABOUT			0x0100
#define OPT_VERBOSE			0x0200

#define	BUFF_CT				1024												//	Number of items processed in each block
#define	CHAN_IN_CT			1													//	Input channel count
#define	CHAN_OUT_CT			2													//	Output channel count

//------------------------------------------------------------------------------
// Local data
//------------------------------------------------------------------------------
static char*			FileName_In;											//  FileName: Input
static char*			FileName_Out;											//  Filename: Output
static poptContext		optCon;													//	Options context (popt)

static	double			i_buff[BUFF_CT*CHAN_IN_CT];								//	Input buffer
static	double			o_buff[BUFF_CT*CHAN_OUT_CT];							//	Output buffer

static	double			param_d[DBL_PARAM_CT];									//	Parameters to be passed to the dsp effect

static	sf_count_t		buff_rd_ct;												//	Read counter
static	sf_count_t		buff_wr_ct;												//	Write counter

//------------------------------------------------------------------------------
// Local functions
//------------------------------------------------------------------------------
static void about(void);

//------------------------------------------------------------------------------
//	Main application
//
//	This application:
//	- opens the input file for reading
//	- creates the output file and prepares it for writing
//	- initializes the dsp effect under test
//	- sends the audio data block-by-block to the dsp effect
//	- writes the resulting audio to the output file
//	- clears out any remaining audio in the effect
//	- closes the input file
//	- closes the output file
//	- exits
//
//	Libraries:
//	- popt: used for argument processing
//	- sndfile: used for sound file I/O
//------------------------------------------------------------------------------
int	main(int argc, char ** argv)
{
	SNDFILE *infile, *outfile;
	SF_INFO i_sfinfo,o_sfinfo;

//------------------------------------------------------------------------------
//  Declare options
//------------------------------------------------------------------------------
	uint32_t	opt=0;
	int			idx=0;

	struct poptOption option_tab[] = {
		{ "in",		'i', POPT_ARG_STRING, &FileName_In,	 'i', "input filename (required)",			NULL	},
		{ "out",	'o', POPT_ARG_STRING, &FileName_Out, 'o', "output filename (required)",			NULL	},
		{ "db",		'd', POPT_ARG_DOUBLE, &param_d[0],	 'd', "attenuation in dB",					NULL	},
		{ "about",	'a', POPT_ARG_NONE,	  NULL,			 'a', "display information about the app",	NULL	},
		{ "verbose",'v', POPT_ARG_NONE,	  NULL,			 'v', "enable verbose mode",				NULL	},
		POPT_AUTOHELP
		POPT_TABLEEND
	};

//------------------------------------------------------------------------------
//  Process arguments
//------------------------------------------------------------------------------
	optCon = poptGetContext(NULL, argc, (const char **)argv, option_tab, 0);
	poptSetOtherOptionHelp(optCon, "[OPTIONS]*");
	if (argc == 1) {
		about();
	}
	while ((idx = poptGetNextOpt(optCon)) >= 0) {
		switch (idx) {
		case 'i':
			opt |= OPT_INPUT;
			break;
		case 'o':
			opt |= OPT_OUTPUT;
			break;
		case 'd':
			opt |= OPT_VOL_DB;
			break;
		case 'a':
			opt |= OPT_ABOUT;
			break;
		case 'v':
			opt |= OPT_VERBOSE;
			break;
		}
	}
	if (idx < -1) {
		fprintf(stderr, "%s: %s\n",
		        poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
		        poptStrerror(idx));
		exit(opt);
	}
	if (opt & OPT_ABOUT) {
		bldinfoPrintBldInfo();
		exit(0);
	}
	if ((opt & OPT_INPUT) == 0) {
		fprintf(stderr, "Input file required.\n");
		exit(10);
	}
	if ((opt & OPT_OUTPUT) == 0) {
		fprintf(stderr, "Output file required.\n");
		exit(11);
	}
	if (opt & OPT_VOL_DB) {														//	attenuation parameter testing
		if (param_d[0] > 0.0) {													//	If the attenuation is larger than zero...
			fprintf(stderr, "attenuation out of range (-96..0).\n");			//	...print error message
			exit(20);															//	...Exit: status 20 (attenuation out of range)
		}
		if (param_d[0] < -100.0) {												//	If the attenuation is greater than one hundred...
			fprintf(stderr, "attenuation out of range (-96..0).\n");			//	...print error message
			exit(21);															//	...Exit: status 21 (attenuation out of range)
		}
	} else {																		//	If attenuation isn't present at all...
		fprintf(stderr, "attenuation required.\n");								//	...print error message
		exit(22);																//	...Exit: status 22 (attenuation required)
	}
	if (opt & OPT_VERBOSE) {
		fprintf(stderr, "Verbose option active!\n");
	}

//------------------------------------------------------------------------------
//	File tests:
//	- Are the file names the same?
//------------------------------------------------------------------------------
	if (strcmp(FileName_In, FileName_Out) == 0) {
		printf("Error : input and output file names are the same.\n");
		exit(1);
	};

//------------------------------------------------------------------------------
//	Open the files
//	- Open input file
//	- Open output file
//------------------------------------------------------------------------------
	memset(&i_sfinfo, 0, sizeof(SF_INFO));										//	Clear the input SF_INFO structure
	memset(&o_sfinfo, 0, sizeof(SF_INFO));										//	Clear the output SF_INFO structure
	if ((infile = sf_open(FileName_In, SFM_READ, &i_sfinfo)) == NULL) {			//	Attempt to open the input file
		printf("Error : Not able to open input file '%s'\n", FileName_In);		//	Fail, then display error
		sf_close(infile);														//	Close out the sound file library
		exit(1);																//	Exit: status 1 (no input file)
	};

	remove(FileName_Out);														//	Delete the output file
	o_sfinfo = i_sfinfo;														//	Copy the input to the output
	o_sfinfo.channels = 2;														//	Stereo output
	if ((outfile = sf_open(FileName_Out, SFM_WRITE, &o_sfinfo)) == NULL) {		//	Attempt to open the output file
		printf("Error : Not able to open output file '%s'\n", FileName_Out);	//	Fail, then display error
		sf_close(infile);														//	Close out the sound file library
		exit(2);																//	Exit: status 2 (cannot open output file)
	};

//------------------------------------------------------------------------------
//	Initialize the DSP effect
//------------------------------------------------------------------------------
	dsp_buff_init(&i_sfinfo,&o_sfinfo);											//	Initialize the effect with the sfinfo structure
	dsp_param_init(param_d);													//	Initialize the parameters: attenuation in dB
	prt_init();																	//	Init the screen update

	memset(i_buff,  0, BUFF_CT*CHAN_IN_CT*sizeof(double));						//	Clear the input buffer
	memset(o_buff, 0, BUFF_CT*CHAN_OUT_CT*sizeof(double));						//	Clear the output buffer

//------------------------------------------------------------------------------
//	Process the audio file with the DSP effect
//------------------------------------------------------------------------------
	while ((buff_rd_ct = sf_read_double(infile, i_buff, BUFF_CT)) > 0) {		//	Read a block of audio from the input file
		dsp_buff_process(o_buff, i_buff, buff_rd_ct);							//	Process the input block to the output block
		dsp_param_process();													//	Update the parameters
		prt_proc();																//	Update the display
		buff_wr_ct = buff_rd_ct*CHAN_OUT_CT;									//	Output is defined by the channel count
		sf_write_double(outfile, o_buff, buff_wr_ct);							//	Write the output file
		memset(o_buff, 0, BUFF_CT*CHAN_OUT_CT*sizeof(double));					//	Clear the output buffer
	}

//------------------------------------------------------------------------------
//	End the DSP effect
//------------------------------------------------------------------------------
	dsp_buff_end();																//	Complete DSP processing
	dsp_param_end();															//	Complete parameter processing
	prt_end();																	//	Complete display processing

//------------------------------------------------------------------------------
//	Close the files
//	- Close input file
//	- Close output file
//------------------------------------------------------------------------------
	sf_close(infile);															//	Close the input file
	sf_close(outfile);															//	Close the output file

	return 0;																	//	Exit: status 0 (no error)
}

//------------------------------------------------------------------------------
//!	@brief
//!		Display about
//!
//!	@param
//!		none
//!
//!	@return
//!		none
//------------------------------------------------------------------------------
static void about(void)
{
	bldinfoPrintBldInfo();
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
}

//
//	End: dsp_base.c
//